#include "argparser.h"
// checked
char* typeToString(Token t) {
	char *result;
	switch (t.type) {
	case COMMAND:
		result = strdup("COMMAND");
		break;
	case PARAMETER:
		result = strdup("PARAMETER");
		break;
	case _EOF:
		result = strdup("EOF");
		break;
	default:
		break;
	}
	return result;
}

// checked
void replToken(Token t) {
	char *type = typeToString(t);
	printf("Token(type:%s,value:%s)", type, t.value);
}

// checked
char* concatArgs(int argc, char *argv[]) {
	char buffer[MAX_LENGTH] = "";
	for (int i = 1; i < argc; i++) {
		strcat(buffer, argv[i]);
		strcat(buffer, " ");
	}
	return strdup(buffer);
}

// checked
Lexer initLexer(Lexer l, int argc, char *argv[]) {
	char *text = concatArgs(argc, argv);
	l.text = text;
	l.pos = 0;
	if (strlen(text) == 0) {
		l.current_char = '\0';
	}
	else {
		l.current_char = text[l.pos];
	}
	return l;
}

// checked
void advance(Lexer *l) {
	l->pos += 1;
	if (l->pos > strlen(l->text) - 1) {
		l->current_char = '\0';
	}
	else {
		// printf("%c\n", l->text[l->pos]);
		l->current_char = l->text[l->pos];
	}
}

void skipWhitespace(Lexer *l) {
	while (l->current_char != '\0' && l->current_char == 32) {
		advance(l);
	}
}
// checked
int isValidChar(char ch) {
	int result = FALSE;
	// form a -> z
	if (ch >= 97 && ch <= 122) {
		result = TRUE;
	}
	// from A -> Z
	if (ch >= 65 && ch <= 90) {
		result = TRUE;
	}
	// . included
	if (ch == 46) {
		result = TRUE;
	}
	if (ch >= 48 && ch <= 57) {
		result = TRUE;
	}
	if(ch == '/'){
		result = TRUE;
	} 
	return result;
}

void checkWhitespace(Lexer *l) {
	if (l->current_char != '\0' && l->current_char == 32) {
		advance(l);
	}
	else if (l->current_char == '\0') {
	}
	else {
		printf("Must separate commands with whitespace");
		exit(-1);
	}
}


// checked
char* command(Lexer *l) {
	// remember to initialize array!!!
	char result_buffer[MAX_LENGTH] = "";
	int current_len = strlen(result_buffer);
	result_buffer[current_len++] = l->current_char;
	advance(l);
	if (l->current_char == '\0') {
		printf("ERROR parsing input");
		exit(-1);
	}
	result_buffer[current_len++] = l->current_char;
	advance(l);
	checkWhitespace(l);
	return strdup(result_buffer);
}


// checked
char* parameter(Lexer *l) {
	// remember to initialize array!!!
	char result_buffer[MAX_LENGTH] = "";
	int current_len = strlen(result_buffer);
	while (l->current_char != '\0' && isValidChar(l->current_char)) {
		result_buffer[current_len++] = l->current_char;
		advance(l);
	}
	return strdup(result_buffer);
}

// checked
Token getNextToken(Lexer *l) {
	while (l->current_char != '\0') {
		if (l->current_char == 32) {
			skipWhitespace(l);
			continue;
		}
		if (l->current_char == '-') {
			Token t = { COMMAND, command(l) };
			return t;
		}
		if (isValidChar(l->current_char)) {
			Token t = { PARAMETER, parameter(l) };
			return t;
		}
		printf("ERROR parsing input");
		exit(-1);
	}
	Token t = { _EOF,'\0' };
	return t;
}

// checked
Command *initCommand() {
	Command *head = (Command *)malloc(sizeof(Command));
	if (!head) {
		printf("malloc space for command failed\n");
		exit(-1);
	}
	head->next = NULL;
	head->command = NULL;
	head->help = NULL;
	return head;
}

// checked
Command *getTailCommand(Command *head) {
	while (head->next != NULL) {
		head = head->next;
	}
	return head;
}

// checked
void putCommand(Command *head, char *command, char *help, int required, int require_parameter) {
	Command *new_command = (Command*)malloc(sizeof(Command));
	if (!new_command) {
		printf("malloc space for command failed\n");
		exit(-1);
	}
	Command *tail = getTailCommand(head);
	new_command->next = NULL;
	new_command->command = command;
	new_command->help = help;
	new_command->required = required;
	new_command->require_parameter = require_parameter;
	new_command->parameter = NULL;
	new_command->triggered = FALSE;
	tail->next = new_command;


	all_commands[number_of_command++] = command;
}

// checked
ParameterInterpreter initParameterInterpreter(Lexer *l, Command *head) {
	ParameterInterpreter pi = { l,getNextToken(l),head };
	return pi;
}

// checked
void eat(ParameterInterpreter *pi, int type) {
	if (pi->current_token.type == type) {
		pi->current_token = getNextToken(pi->lexer);
	}
	else {
		printf("ERROR parsing input");
		exit(-1);
	}
}

// checked
int isCommandExist(char *value) {
	int result = FALSE;
	for (int i = 0; i < number_of_command; ++i)
	{
		if (!strcmp(value, all_commands[i])) {
			result = TRUE;
		}
	}
	return result;
}

void switchTriggerState(Command *head, Token t) {
	for (head = head->next; head != NULL; head = head->next)
	{
		if (!strcmp(t.value, head->command)) {
			head->triggered = TRUE;
			return;
		}
	}
}

Token base(ParameterInterpreter *pi) {
	Token t = pi->current_token;
	if (isCommandExist(t.value)) {
		eat(pi, COMMAND);
		switchTriggerState(pi->head, t);
	}
	else {
		printf("%s command not defined\n",t.value);
		exit(-1);
	}
	return t;
}

// checked
Command *findCommandByName(Command *head, char *command) {
	for ( head = head->next; head != NULL; head = head->next) {
		if (!strcmp(head->command, command)) {
			return head;
		}
	}
	return NULL;
}

void combination(ParameterInterpreter *pi) {
	Token command_token = base(pi);
	Token t = pi->current_token;
	Command *current_command = findCommandByName(pi->head, command_token.value);
	if (current_command->require_parameter == TRUE && t.type == PARAMETER) {
		eat(pi, PARAMETER);
		current_command->parameter = t.value;
	}
	else if(current_command->require_parameter == FALSE && t.type == PARAMETER){
		printf("%s does not take a parameter\n",command_token.value);
		exit(-1);
	}
	else if(current_command->require_parameter == FALSE){
		return;
	}
	else{
		printf("%s requires a parameter\n",command_token.value );
		exit(-1);
	}
}

Command *handleParameter(ParameterInterpreter *pi){
	while (pi->current_token.type == COMMAND){
			combination(pi);
	}
	if( pi->current_token.type != _EOF){
		printf("ERROR parsing input\n");
	}
	return pi->head;
}

Command *getTriggeredComandList(Command *head){
	Command *new_head = (Command*)malloc(sizeof(Command));
	new_head->next = NULL;
	Command *current_node = new_head;
	for(head = head->next;head!=NULL;head = head -> next){
		if (head->triggered == TRUE){
			Command *node = (Command*) malloc(sizeof(Command));
			// could not figure out a better way
			memcpy(node,head,sizeof(Command));
			node->next = NULL;
			current_node->next = node;
			current_node = node;
		}
	}
	return new_head;
}

void replCommandList(Command *head){
	for(head = head->next;head!=NULL;head = head->next){
		printf("Command:%s\n",head->command );
		printf("Required:%s\n",head->required == TRUE?"TRUE":"FALSE");
		printf("Required Parameter:%s\n",head->require_parameter == TRUE?"TRUE":"FALSE");
		printf("Parameter:%s\n",head->parameter== NULL?"NONE":head->parameter );
		
		printf("Help:%s\n",head->help);
	}
}
